
#ifndef _EXTREAM_COMMON_ARRAY3D_HPP_
#define _EXTREAM_COMMON_ARRAY3D_HPP_

#include <cassert>
#include <cstring>
// #include <type_traits>
// FORTRAN 格式的索引是 列优先 如 a(2,3,4) 为2行3列4页
// 即一列有两个元素，一行有3个元素  页有四个（页）

// c 格式为 2页 3行 四列  注意区分 行优先


template <typename T> class Array3d {
private:
  int dims_[3]{0};
  long j_stride_{0};
  long k_stride_{0};
  long size_{0};
  bool is_owner_{true};
  // 经常要引用外部数据,所以这里使用指针
  T *elem_{nullptr};

  void copy_from(const Array3d &src);
  void move_from(Array3d &src);
  void init_with_ijk(int i, int j, int k) {
    dims_[0] = i;
    dims_[1] = j;
    dims_[2] = k;
    j_stride_ = i;
    k_stride_ = i * j;
    size_ = k_stride_ * k;
  }

public:
  Array3d(){};
  Array3d(int i, int j, int k)
      : dims_{i, j, k}, j_stride_{i}, k_stride_{i * j}, size_{k_stride_ * k},
        is_owner_{true}, elem_{new T[size_]} {}

  Array3d(int ijk[3]) : Array3d(ijk[0], ijk[1], ijk[2]) {}
  Array3d(const Array3d &src) { copy_from(src); }
  Array3d &operator=(const Array3d &src);
  Array3d(Array3d &&src) { move_from(src); }
  Array3d &operator=(Array3d &&src);
  ~Array3d() {
    if (is_owner_)
      delete[] elem_;
    init_with_ijk(0, 0, 0);
  }

  T &operator()(int i, int j, int k) {
    assert(-1 < i && i < dims_[0] && -1 < j && j < dims_[1] && -1 < k &&
           k < dims_[2]);
    return elem_[i + j * j_stride_ + k * k_stride_];
  }
  const T &operator()(int i, int j, int k) const {
    assert(-1 < i && i < dims_[0] && -1 < j && j < dims_[1] && -1 < k &&
           k < dims_[2]);
    return elem_[i + j * j_stride_ + k * k_stride_];
  }

  T &operator()(int ijk[3]) {
    assert(-1 < ijk[0] && ijk[0] < dims_[0] && -1 < ijk[1] &&
           ijk[1] < dims_[1] && -1 < ijk[2] && ijk[2] < dims_[2]);
    return elem_[ijk[0] + ijk[1] * j_stride_ + ijk[2] * k_stride_];
  }
  const T &operator()(int ijk[3]) const {
    assert(-1 < ijk[0] && ijk[0] < dims_[0] && -1 < ijk[1] &&
           ijk[1] < dims_[1] && -1 < ijk[2] && ijk[2] < dims_[2]);
    return elem_[ijk[0] + ijk[1] * j_stride_ + ijk[2] * k_stride_];
  }

  T &operator[](const long &idx) {
    assert(-1 < idx && idx < size_);
    return elem_[idx];
  }
  const T &operator[](const long &idx) const {
    assert(-1 < idx && idx < size_);
    return elem_[idx];
  }

  T &operator()(const long &idx) {
    assert(-1 < idx && idx < size_);
    return elem_[idx];
  }
  const T &operator()(const long &idx) const {
    assert(-1 < idx && idx < size_);
    return elem_[idx];
  }

  void operator=(const T &val) {
    for (long i{0}; i < size_; ++i)
      elem_[i] = val;
  }
  void copyValue(const Array3d &src) {
    assert(size_ == src.size_ && dims_[0] == src.dims_[0] &&
           dims_[1] == src.dims_[1] && dims_[2] == src.dims_[2]);
    for (long i{0}; i < size_; ++i)
      elem_[i] = src.elem_[i];
  }
  // 不能使用 const
  void bind(T *data, int i, int j, int k) {
    delete[] elem_;
    elem_ = data;
    init_with_ijk(i, j, k);
    is_owner_ = false;
  }

  void init(int i, int j, int k) {
    assert(is_owner_ && elem_ == nullptr);
    init_with_ijk(i, j, k);
    elem_ = new T[size_];
  }
};

template <typename T> void Array3d<T>::copy_from(const Array3d &src) {
  init_with_ijk(src.dims_[0], src.dims_[1], src.dims_[2]);
  is_owner_ = src.is_owner_;
  elem_ = new T[size_];
  mempcpy(elem_, src.elem_, sizeof(T) * size_);
}

template <typename T> void Array3d<T>::move_from(Array3d &src) {
  init_with_ijk(src.dims_[0], src.dims_[1], src.dims_[2]);
  is_owner_ = src.is_owner_;
  elem_ = src.elem_;

  src.init_with_ijk(0, 0, 0);
  src.elem_ = nullptr;
}

template <typename T> Array3d<T> &Array3d<T>::operator=(const Array3d &src) {
  if (this == &src)
    return *this;
  delete[] elem_;
  copy_from(src);
  return *this;
}

template <typename T> Array3d<T> &Array3d<T>::operator=(Array3d &&src) {
  if (this == &src)
    return *this;
  delete[] elem_;
  move_from(src);
}

#endif